package com.mia.craftstudio.minecraft;

import com.mia.craftstudio.CSModel;
import com.mia.craftstudio.libgdx.Vector3;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;

import java.nio.FloatBuffer;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class CraftStudioRendererVBO {
   private Set subRenderers = new HashSet();
   private int[] size = new int[3];
   private CSModel.Point[] textureUV;
   private int[] texSize = new int[2];
   private byte[] UVTransform = new byte[6];
   private boolean isTransparent;
   private CSModel.ModelNode node;
   private CraftStudioModelWrapper.NodeWrapper wrappedNode;
   private boolean ao;
   static final int[][] vertexLineStripsIndexes = new int[][]{{0, 1, 3, 2, 0}, {4, 5, 7, 6, 4}, {1, 5, 7, 3, 1}, {0, 4, 6, 2, 0}};
   static final int[][] vertexQuadsIndexes = new int[][]{{0, 2, 6, 4}, {5, 7, 3, 1}, {4, 6, 7, 5}, {2, 3, 7, 6}, {1, 3, 2, 0}, {1, 0, 4, 5}};
   static final int[][] vertexTriangleStripIndexes = new int[][]{{2, 2}, {2, 6, 0, 4}, {4, 6, 5, 7}, {7, 3, 5, 1}, {1, 3, 0, 2}, {2, 3, 6, 7}, {7, 0}, {0, 4, 1, 5}, {5, 5}};
   static final int[][] UVPairs = new int[][]{{0, 0}, {0, 1}, {1, 1}, {1, 0}};
   static final int[][] UVSetDegen = new int[][]{{0, 0}, {0, 0}, {0, 0}, {0, 0}};
   static final int[][][] UVSet = new int[][][]{{{1, 2, 0, 3}, {0, 1, 3, 2}, {3, 0, 2, 1}, {2, 3, 1, 0}}, {{0, 1, 3, 2}, {3, 0, 2, 1}, {2, 3, 1, 0}, {1, 2, 0, 3}}, {{2, 1, 3, 0}, {1, 0, 2, 3}, {0, 3, 1, 2}, {3, 2, 0, 1}}, {{3, 2, 0, 1}, {2, 1, 3, 0}, {1, 0, 2, 3}, {0, 3, 1, 2}}, {{0, 3, 1, 2}, {3, 2, 0, 1}, {2, 1, 3, 0}, {1, 0, 2, 3}}, {{1, 0, 2, 3}, {0, 3, 1, 2}, {3, 0, 2, 1}, {2, 1, 3, 0}}, {{3, 0, 2, 1}, {2, 3, 1, 0}, {1, 2, 0, 3}, {0, 1, 3, 2}}, {{2, 3, 1, 0}, {1, 2, 0, 3}, {0, 1, 3, 2}, {3, 0, 2, 1}}};
   static final int[][][][] vertexUVsCoordsTriangleStrip;
   static final int[] convertionTable;
   static final int[][][] rotationTransformTable;

   public CraftStudioRendererVBO(CraftStudioModelWrapper.NodeWrapper wrappedNode) {
      this.wrappedNode = wrappedNode;
      this.node = wrappedNode.node;
      this.isTransparent = this.node.hasAttribute(CSModel.ModelNode.Attrb.TRANSPARENT);
      this.ao = this.node.hasAttribute(CSModel.ModelNode.Attrb.AMBIENTOCCLUSION);

      for(int i = 0; i < 3; ++i) {
         this.size[i] = this.node.getSize()[i];
      }

      this.texSize[0] = this.node.getTexture().getWidth();
      this.texSize[1] = this.node.getTexture().getHeight();
      this.textureUV = this.node.getQuads();
      this.UVTransform = this.node.getUVTransform();
      Iterator var4 = this.node.getChildren().iterator();

      while(var4.hasNext()) {
         CSModel.ModelNode node_ = (CSModel.ModelNode)var4.next();
         this.subRenderers.add(new CraftStudioRendererVBO(wrappedNode.modelWrapper.nodeCache.get(node_)));
      }

   }

   public CSModel.ModelNode getNode() {
      return this.node;
   }

   @SideOnly(Side.CLIENT)
   private float[][] generateUVs() {
      float[][] outArray = new float[6][4];
      CSModel.Point[] texUV = new CSModel.Point[6];
      int[][][] texOffsetsX = new int[][][]{{{0, this.size[0]}, {0, this.size[0]}, {0, this.size[2]}, {0, this.size[0]}, {0, this.size[2]}, {0, this.size[0]}}, {{this.size[0], 0}, {this.size[0], 0}, {this.size[2], 0}, {this.size[0], 0}, {this.size[2], 0}, {this.size[0], 0}}};
      int[][][] texOffsetsY = new int[][][]{{{0, this.size[1]}, {0, this.size[1]}, {0, this.size[1]}, {0, this.size[2]}, {0, this.size[1]}, {0, this.size[2]}}, {{this.size[1], 0}, {this.size[1], 0}, {this.size[1], 0}, {this.size[2], 0}, {this.size[1], 0}, {this.size[2], 0}}};
      int indexRot = CraftStudioRendererVBO.UVTrans.getRotationIndex(this.UVTransform[0]);
      int indexMir = CraftStudioRendererVBO.UVTrans.getMirrorIndex(this.UVTransform[0]);
      float[] scale = this.node.getScale();
      int[] convertTable = new int[]{0, 1, 2, 3, 4, 5};
      int texInvertedX = 0;
      int texInvertedY = 0;
      if (scale[0] < 0.0F) {
         texInvertedX = 1;
         convertTable[2] = 4;
         convertTable[4] = 2;
      }

      if (scale[1] < 0.0F) {
         texInvertedY = 1;
         convertTable[3] = 5;
         convertTable[5] = 3;
      }

      if (scale[2] < 0.0F) {
         convertTable[0] = 1;
         convertTable[1] = 0;
      }

      int i;
      for(i = 0; i < 6; ++i) {
         int index = convertTable[i];
         texUV[i] = new CSModel.Point(this.textureUV[index].x - texOffsetsX[texInvertedX][index][1] * rotationTransformTable[0][indexRot][indexMir], this.textureUV[index].y - texOffsetsY[texInvertedY][index][1] * rotationTransformTable[1][indexRot][indexMir]);
      }

      for(i = 0; i < 6; ++i) {
         float u1 = (float)(texUV[i].x + texOffsetsX[texInvertedX][i][0]) / (float)this.texSize[0];
         float u2 = (float)(texUV[i].x + texOffsetsX[texInvertedX][i][1]) / (float)this.texSize[0];
         float v1 = (float)(texUV[i].y + texOffsetsY[texInvertedY][i][0]) / (float)this.texSize[1];
         float v2 = (float)(texUV[i].y + texOffsetsY[texInvertedY][i][1]) / (float)this.texSize[1];
         outArray[i][0] = u1;
         outArray[i][1] = u2;
         outArray[i][2] = v1;
         outArray[i][3] = v2;
      }

      return outArray;
   }

   @SideOnly(Side.CLIENT)
   public void generateVBOTriangleStripInterleaved(FloatBuffer buffer, int rot) {
      float r = 1.0F;
      float g = 1.0F;
      float b = 1.0F;
      if (this.node.hasAttribute(CSModel.ModelNode.Attrb.COLORPRIMARY) && this.wrappedNode.modelWrapper.colorPrimary != null) {
         r = (float)this.wrappedNode.modelWrapper.colorPrimary.getRed() / 255.0F;
         g = (float)this.wrappedNode.modelWrapper.colorPrimary.getGreen() / 255.0F;
         b = (float)this.wrappedNode.modelWrapper.colorPrimary.getBlue() / 255.0F;
      } else if (this.node.hasAttribute(CSModel.ModelNode.Attrb.COLORSECONDARY) && this.wrappedNode.modelWrapper.colorSecondary != null) {
         r = (float)this.wrappedNode.modelWrapper.colorSecondary.getRed() / 255.0F;
         g = (float)this.wrappedNode.modelWrapper.colorSecondary.getGreen() / 255.0F;
         b = (float)this.wrappedNode.modelWrapper.colorSecondary.getBlue() / 255.0F;
      } else if (this.node.hasAttribute(CSModel.ModelNode.Attrb.COLORACCENTPRIMARY) && this.wrappedNode.modelWrapper.colorAccentPrimary != null) {
         r = (float)this.wrappedNode.modelWrapper.colorAccentPrimary.getRed() / 255.0F;
         g = (float)this.wrappedNode.modelWrapper.colorAccentPrimary.getGreen() / 255.0F;
         b = (float)this.wrappedNode.modelWrapper.colorAccentPrimary.getBlue() / 255.0F;
      } else if (this.node.hasAttribute(CSModel.ModelNode.Attrb.COLORACCENTSECONDARY) && this.wrappedNode.modelWrapper.colorAccentSecondary != null) {
         r = (float)this.wrappedNode.modelWrapper.colorAccentSecondary.getRed() / 255.0F;
         g = (float)this.wrappedNode.modelWrapper.colorAccentSecondary.getGreen() / 255.0F;
         b = (float)this.wrappedNode.modelWrapper.colorAccentSecondary.getBlue() / 255.0F;
      }

      this.generateVBOTriangleStripInterleaved(buffer, rot, r, g, b);
   }

   @SideOnly(Side.CLIENT)
   public void generateVBOTriangleStripInterleaved(FloatBuffer buffer, int rot, float r, float g, float b) {
      Vector3[] vertices = this.wrappedNode.getVertices(rot);
      float[][] UVs = this.generateUVs();
      EnumSet transforms = CraftStudioRendererVBO.UVTrans.getUVTransform(this.UVTransform[0]);
      int indexRot = CraftStudioRendererVBO.UVTrans.getRotationIndex(this.UVTransform[0]);
      int indexMir = CraftStudioRendererVBO.UVTrans.getMirrorIndex(this.UVTransform[0]);

      for(int i = 0; i < vertexTriangleStripIndexes.length; ++i) {
         int[] vertexIndexes = vertexTriangleStripIndexes[i];
         Vector3 v1;
         if (vertexIndexes.length > 2) {
            v1 = (new Vector3(vertices[vertexIndexes[1]].x, vertices[vertexIndexes[1]].y, vertices[vertexIndexes[1]].z)).sub(new Vector3(vertices[vertexIndexes[0]].x, vertices[vertexIndexes[0]].y, vertices[vertexIndexes[0]].z));
            Vector3 v2 = (new Vector3(vertices[vertexIndexes[2]].x, vertices[vertexIndexes[2]].y, vertices[vertexIndexes[2]].z)).sub(new Vector3(vertices[vertexIndexes[0]].x, vertices[vertexIndexes[0]].y, vertices[vertexIndexes[0]].z));
            v1.crs(v2).nor();
         } else {
            v1 = (new Vector3(1.0F, 1.0F, 1.0F)).nor();
         }

         int[] verticesUVs = vertexUVsCoordsTriangleStrip[i][indexMir][indexRot];

         for(int j = 0; j < vertexIndexes.length; ++j) {
            Vector3 vertex = vertices[vertexIndexes[j]];
            int vertexUV = verticesUVs[j];
            buffer.put(vertex.x).put(vertex.y).put(vertex.z);
            buffer.put(v1.x).put(v1.y).put(v1.z);
            buffer.put(UVPairs[vertexUV][0] == 0 ? UVs[convertionTable[i]][0] : UVs[convertionTable[i]][1]).put(UVPairs[vertexUV][1] == 0 ? UVs[convertionTable[i]][2] : UVs[convertionTable[i]][3]);
            buffer.put(r).put(g).put(b);
         }
      }

   }

   @SideOnly(Side.CLIENT)
   public void generateVBOLineStrip(FloatBuffer buffer, int rot) {
      boolean isFlat = this.wrappedNode.isFlat();
      Vector3[] vertices = this.wrappedNode.getVertices(rot);

      for(int i = 0; i < vertexLineStripsIndexes.length; ++i) {
         int[] vertexIndexes = vertexLineStripsIndexes[i];

         for(int j = 0; j < vertexIndexes.length; ++j) {
            Vector3 vertex = vertices[vertexIndexes[j]];
            if (!isFlat) {
               buffer.put(vertex.x).put(vertex.y).put(vertex.z);
            } else {
               buffer.put(vertices[0].x).put(vertices[0].y).put(vertices[0].z);
            }
         }
      }

   }

   @SideOnly(Side.CLIENT)
   public void collectRenderers(Set renderers1st, Set renderers2nd) {
      if (!this.isTransparent) {
         renderers1st.add(this);
      } else {
         renderers2nd.add(this);
      }

      Iterator var3 = this.subRenderers.iterator();

      while(var3.hasNext()) {
         CraftStudioRendererVBO subRenderer = (CraftStudioRendererVBO)var3.next();
         subRenderer.collectRenderers(renderers1st, renderers2nd);
      }

   }

   static {
      vertexUVsCoordsTriangleStrip = new int[][][][]{{UVSetDegen, UVSetDegen, UVSetDegen, UVSetDegen}, {UVSet[0], UVSet[2], UVSet[4], UVSet[6]}, {UVSet[1], UVSet[3], UVSet[5], UVSet[7]}, {UVSet[0], UVSet[2], UVSet[4], UVSet[6]}, {UVSet[1], UVSet[3], UVSet[5], UVSet[7]}, {UVSet[1], UVSet[3], UVSet[5], UVSet[7]}, {UVSetDegen, UVSetDegen, UVSetDegen, UVSetDegen}, {UVSet[0], UVSet[2], UVSet[4], UVSet[6]}, {UVSetDegen, UVSetDegen, UVSetDegen, UVSetDegen}};
      convertionTable = new int[]{0, 0, 2, 1, 4, 3, 5, 5, 5};
      rotationTransformTable = new int[][][]{{{0, 1, 0, 1}, {1, 1, 0, 0}, {1, 0, 1, 0}, {0, 0, 1, 1}}, {{0, 0, 1, 1}, {0, 1, 0, 1}, {1, 1, 0, 0}, {1, 0, 1, 0}}};
   }

   private static enum UVTrans {
      Rotation_0,
      Rotation_90,
      Rotation_180,
      Rotation_270,
      Mirror_H,
      Mirror_V;

      public static EnumSet getUVTransform(int value) {
         EnumSet ret = EnumSet.noneOf(CraftStudioRendererVBO.UVTrans.class);
         if ((value & 1) == 1) {
            ret.add(Rotation_90);
         }

         if ((value & 2) == 2) {
            ret.add(Rotation_180);
         }

         if ((value & 4) == 4) {
            ret.add(Rotation_270);
         }

         if (ret.isEmpty()) {
            ret.add(Rotation_0);
         }

         if ((value & 8) == 8) {
            ret.add(Mirror_H);
         }

         if ((value & 16) == 16) {
            ret.add(Mirror_V);
         }

         return ret;
      }

      public static int getRotationIndex(int value) {
         EnumSet transforms = getUVTransform(value);
         if (transforms.contains(Rotation_0)) {
            return 0;
         } else if (transforms.contains(Rotation_90)) {
            return 1;
         } else if (transforms.contains(Rotation_180)) {
            return 2;
         } else {
            return transforms.contains(Rotation_270) ? 3 : -1;
         }
      }

      public static int getMirrorIndex(int value) {
         EnumSet transforms = getUVTransform(value);
         if (transforms.contains(Mirror_H) && transforms.contains(Mirror_V)) {
            return 3;
         } else if (transforms.contains(Mirror_V)) {
            return 2;
         } else {
            return transforms.contains(Mirror_H) ? 1 : 0;
         }
      }
   }
}
